<template>
  <div
    ref="catalog"
    id="catalog"
    class="catalog__default"
    :class="catalogFixed ? 'catalog__fixed' : ''">
    <p class="mb-2 text-h6 font-weight-medium text--primary">{{ title }}</p>
    <ul>
      <li
        class="pl-5 text-body-2 py-2 font-weight-regular catalog--default"
        :class="intersectingItemId === item.id ? 'catalog--active' : ''"
        v-for="(item, index) in catalogItemList"
        :key="index"
        @click="handleCatalogClick(item.id)">
        {{ item.title }}
        <ul v-if="item.children">
          <li
            class="text-body-2 pl-9 py-2 font-weight-regular catalog--default"
            :class="intersectingItemId === child.id ? 'catalog--active' : ''"
            v-for="(child, childIndex) in item.children"
            :key="childIndex"
            @click.stop="handleCatalogClick(child.id)">
            {{ child.title }}
          </li>
        </ul>
      </li>
    </ul>
  </div>
</template>

<script>
    /**
     * @description 目录组件
     * @property {Array} value 目录项数组 [{
     *   id: String|Number, //节点id，必须提供不可重复的id
     *   title: String, //目录项文字内容
     *   children: Array //二级目录项，与目录项数组格式一致，可选
     * }]
     * @property {Boolean} fixed 是否固定目录组件（更改为fixed显示）
     * @property {String} title 目录组件的标题，默认为“目录”
     * @event catalogClick(id) 目录项点击回调事件
     * @method onIntersect(id, isIntersecting) 相交区改变事件（从父组件调用，在onIntersect的回调函数中调用）
     * @example <catalog ref="catalog" v-model="..." :fixed="..." @catalogClick="..."/>
     *
     */
    export default {
        name: "catalog",
        props: {
            value: {
                type: Array,
                default: []
            },
            fixed: {
                type: Boolean,
                default: false
            },
            title: {
                type: String,
                default: '目录'
            },
        },
        data() {
            return {
                catalogItemList: [], //目录项列表
                intersectTrigger: true, //相交触发器，用于拦截点击目录项后一定时间内的因页面滚动触发的相交区改变事件
                clickDelayTimer: null, //点击目录项后延迟触发相交触发器的计时器
            }
        },
        methods: {
            /**
             * 相交区改变事件（从父组件调用，在onIntersect的回调函数中调用）
             * @param {String} id 发生交互区变化的目录项的id
             * @param {Boolean} isIntersecting 交互区变化回调事件中的isIntersecting，表示元素是否正在与可视区域交互
             */
            onIntersect(id, isIntersecting) {
                if (this.intersectTrigger && this.clickDelayTimer === null) {
                    this.catalogItemList.find(item => {
                        if (item.id === id) {
                            this.$set(item, 'isIntersecting', isIntersecting);
                        }
                        else if (item.children) {
                            item.children.find(child => {
                                if (child.id === id) {
                                    this.$set(child, 'isIntersecting', isIntersecting);
                                }
                            });
                        }
                    });
                }
            },
            /**
             * 目录项点击事件
             * @param {String} id 当前触发点击事件的目录项的id
             */
            handleCatalogClick(id) {
                if (id) {
                    this.intersectTrigger = false;
                    this.clickDelayTimer = setTimeout(() => {
                        this.intersectTrigger = true;
                        clearTimeout(this.clickDelayTimer);
                        this.clickDelayTimer = null;
                    }, 500); //点击延迟计时器，用于拦截点击目录项后一定时间内的因页面滚动触发的相交区改变事件
                    this.$emit('catalogClick', id);
                    this.setIntersecting(id);
                }
            },
            /**
             * 手动设置某菜单项为正在交互
             * @param {String} id 设置目标菜单项的id
             */
            setIntersecting(id) {
                let trigger = false;
                this.catalogItemList.forEach(item => {
                    if (item.children) {
                        item.children.forEach(child => {
                            if (child.id === id) {
                                child.isIntersecting = true;
                                trigger = true;
                            }
                            else {
                                child.isIntersecting = false;
                            }
                        });
                    }
                    item.isIntersecting = trigger ? false : item.id === id;
                });
            }
        },
        computed: {
            // 页面滚动高度
            scrollTop() {
                if (!this.intersectTrigger) {
                    this.intersectTrigger = true;
                }
                return this.$store.state.scrollTop;
            },
            // 映射props中的value的值
            getCatalogItemList: {
                get() {
                    this.catalogItemList = this.value;
                    return this.value;
                }
            },
            // 映射props中的fixed的值
            catalogFixed: {
                get() {
                    return this.fixed;
                }
            },
            // 当前正在交互的目录项ID，用于更新目录项样式
            intersectingItemId: {
                get() {
                    let currentItem = null, catalogItem = null;
                    catalogItem = this.catalogItemList.find(item => {
                        if (item.children) {
                            currentItem = !currentItem ? item.children.find(child => child.isIntersecting) : currentItem;
                            return !!currentItem ? null : item.isIntersecting;
                        }
                        else {
                            return item.isIntersecting;
                        }
                    });
                    return !!currentItem ? currentItem.id : !!catalogItem ? catalogItem.id : '';
                }
            },
            catalogWidth: {
                get() {
                    if (this.$refs.catalog) {
                        return this.$refs.catalog.clientWidth;
                    }
                }
            }
        },
        mounted() {
            this.catalogItemList = this.value;
        }
    }
</script>

<style lang="scss" scoped>
  @import 'catalog';
</style>